%class-name Parser
%class-header Parser.h
%implementation-header Parser.ih
%parsefun-source Parser.cpp
%scanner Scanner.h

%baseclass-preinclude engine/CmdEngine.h

%stype QVariant

// lower precedence
%token ID STRING_CONST INT_CONST FLOAT_CONST
%token IMPORT AS OBJ_ID
%left SEMICOLON
%left '+' '-'
%left '*' '/'
%right UNARY
// higher precedence

%%

input:
    imports object {
        TODO_LOG(
            "root object is",
            $2.value<QObject*>()->property("id").toString().toStdString()
        );
        Command c(Command::ROOTOBJ);
        c.setArgs(1, &$2);
        cmdEngine.singleCommand(c);
    }
    | error
;

imports:
    import SEMICOLON imports
    | import SEMICOLON
;

import:
    IMPORT ID {
        TODO_LOG("import", $2.toString().toStdString());
        Command c(Command::IMPORT);
        c.setArgs(1, &$2);
        cmdEngine.singleCommand(c);
    }
    | IMPORT STRING_CONST AS ID {
        TODO_LOG2("import", $2.toString().toStdString(), "as", $4.toString().toStdString());
    }
;

object:
    ID obj_body {
        TODO_LOG("object", $1.toString().toStdString());
        cmdEngine.createObject($1.toString(), $2.value<Commands>());

        $$ = QVariant::fromValue(cmdEngine.thisObj());
        //QVariant value = QVariant::fromValue(id);
        // leave reminder with for outer object (for setParent(), etc)
        //Command c(Command::OBJECT);
        //c.setArgs(1, &value);
        //$$ = QVariant::fromValue(c);
    }
;

obj_body:
    '{' statements '}' {
        $$ = $2;
    }
;

statements:
    // empty
    | statement SEMICOLON statements {
        //TODO_LOG("next statement", $1.toString().toStdString());
        Commands cmds($3.value<Commands>());
        cmds.append($1.value<Command>());
        $$ = QVariant::fromValue(cmds);
    }
    | statement SEMICOLON {
        //TODO_LOG("last statement", $1.toString().toStdString());
        Commands cmds;
        cmds.append($1.value<Command>());
        $$ = QVariant::fromValue(cmds);
    }
;

statement:
    // empty
    | assignment
    | object
    | idassign
    /*| handler
    | signal
    | function*/
;

idassign:
    OBJ_ID ':' ID {
        TODO_LOG("object id", $3.toString().toStdString());
        Command c(Command::IDASSIGN);
        c.setArgs(1, &$3);
        $$ = QVariant::fromValue(c);
    }
;

assignment:
    ID ':' expr {
        TODO_LOG2("assign", $3.toString().toStdString(), "to", $1.toString().toStdString());
        Command c(Command::ASSIGN);
        c.setArgs(2, &$1, &$3);
        $$ = QVariant::fromValue(c);
    }
;

expr:
    string_expr
    | int_expr
    | object // anonymous object as result of expression
    | array
    | FLOAT_CONST
    | ID
;

array:
    '[' obj_list ']' {
        TODO_LOG("array", "X");
        $$ = $2;
    }
;

obj_list:
    // empty. TODO: replace with expr_list
    | obj_list ',' object {
        QList<QVariant> v($1.value< QList<QVariant> >());
        v.append($1);
        $$ = QVariant::fromValue(v);
    }
    | obj_list ',' ID {
        QList<QVariant> v($1.value< QList<QVariant> >());
        QObject *obj = cmdEngine.getObjById($3.toString());
        v.append(QVariant::fromValue(obj));
        $$ = QVariant::fromValue(v);
    }
    | ID {
        QObject *obj = cmdEngine.getObjById($1.toString());
        QList<QVariant> v;
        v.append(QVariant::fromValue(obj));
        $$ = QVariant::fromValue(v);
    }
    | object {
        QList<QVariant> v;
        v.append($1);
        $$ = QVariant::fromValue(v);
    }
;


string_expr:
    string_expr '+' string_expr {
        $$ = $1.toString() + $3.toString();
    }
    | STRING_CONST
;

int_expr:
    int_expr '+' int_expr {
        $$ = $1.toInt() + $3.toInt();
        TODO_LOG2("add", QString($1.toString() + "+" + $3.toString()).toStdString(), "results in", $$.toString().toStdString());
    }
    | int_expr '-' int_expr {
        $$ = $1.toInt() - $3.toInt();
        TODO_LOG2("sub", QString($1.toString() + "-" + $3.toString()).toStdString(), "results in", $$.toString().toStdString());
    }
    | int_expr '*' int_expr {
        $$ = $1.toInt() * $3.toInt();
        TODO_LOG2("mul", QString($1.toString() + "*" + $3.toString()).toStdString(), "results in", $$.toString().toStdString());
    }
    | int_expr '/' int_expr {
        $$ = $1.toInt() / $3.toInt();
        TODO_LOG2("div", QString($1.toString() + "/" + $3.toString()).toStdString(), "results in", $$.toString().toStdString());
    }
    | '-' int_expr %prec UNARY {
        $$ = - $2.toInt();
    }
    | '(' int_expr ')' {
        $$ = $2;
    }
    | INT_CONST
;

    /*handler:
        //empty
    ;

    signal:
        //empty
    ;

    function:
        //empty
    ;*/
